/*

    Support for the Jelbert GeoTagger JTR data file format.

    Copyright (C) 2008 Olaf Klein, o.b.klein@gpsbabel.org

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA

 */

#include <ctype.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include "defs.h"
#include "csv_util.h"
#include "avltree.h"

#define MYNAME "jtr"

static
arglist_t jtr_args[] = {
	ARG_TERMINATOR
};

static gbfile *fin, *fout;
static avltree_t *trkpts;

static time_t
jtr_parse_time(const char *str, struct tm *tm, int *micro)
{
	long int hms;
	char *dot;

	hms = strtol(str, &dot, 10);
	if (hms > 0) {
		tm->tm_sec = hms % 100;
		hms = hms / 100;
		tm->tm_min = hms % 100;
		hms = hms / 100;
		tm->tm_hour = hms % 100;

		if ((*dot == '.') && (micro != NULL)) *micro = atoi(dot + 1) * 10000;

		return mkgmtime(tm);
	}
	else return 0;
}

static time_t
jtr_parse_date(const char *str, struct tm *tm)
{
	int dmy = atoi(str);

	if (dmy > 0) {
		tm->tm_year = dmy % 100 + 100;
		dmy = dmy / 100;
		tm->tm_mon  = dmy % 100 - 1;
		dmy = dmy / 100;
		tm->tm_mday = dmy;
		return mkgmtime(tm);
	}
	else return 0;
}

/*******************************************************************************
* %%%        global callbacks called by gpsbabel main process              %%% *
*******************************************************************************/

static void
jtr_rd_init(const char *fname)
{
	fin = gbfopen(fname, "r", MYNAME);
	trkpts = avltree_init(0, MYNAME);
}

static void
jtr_rd_deinit(void)
{
	avltree_done(trkpts);
	gbfclose(fin);
}

static void
jtr_read(void)
{
	char *str;
	int line = 0;
	route_head *trk = NULL;

	while ((str = gbfgetstr(fin))) {
		waypoint *wpt;
		struct tm tm;
		char *tmp;
		int column = -1;
		char valid = 'V';
		double lat, lon;
		float speed, course, mcourse, mvar, mdev;
		time_t time = 0;
		int micros = 0;
		char buf[32];
		char mvardir, mdevdir;

		line++;

		str = lrtrim(str);
		if (*str == '\0') continue;

		if (strncmp(str, "GEOTAG2,", 8) != 0)
			fatal(MYNAME ": Unknown or unsupported file (missing \"GEOTAG2\")!\n");

		memset(&tm, 0, sizeof(tm));
		lat = lon = 999;
		speed = course = mcourse = mvar = mdev = -1;
		mvardir = mdevdir = 0;

		column = -1;
		tmp = str;
		while ((str = csv_lineparse(tmp, ",", "", column++))) {
			tmp = NULL;

			if (*str == '\0') continue;

			switch(column) {
				case 0: break;		/* GEOTAG2 */
				case 1: jtr_parse_time(str, &tm, &micros); break;
				case 2: valid = *str; break;
				case 3: lat = ddmm2degrees(atof(str)); break;
				case 4: if (*str == 'S') lat *= -1; break;
				case 5: lon = ddmm2degrees(atof(str)); break;
				case 6: if (*str == 'W') lon *= -1; break;
				case 7: speed = KNOTS_TO_MPS(atof(str)); break;
				case 8: course = atof(str); break;
				case 9: jtr_parse_date(str, &tm); break;
				case 13: mcourse = atof(str); break;
				case 14: mdev = atof(str); break;
				case 15: mdevdir = *str; break;
				case 16: mvar = atof(str); break;
				case 17: mvardir = *str; break;
				default:
					break;
			}
		}

		if ((lat == 999) || (lon == 999) || (valid != 'A')) continue;

		if (tm.tm_year > 0) time = mkgmtime(&tm);
		else time = 0;

		/* check for duplicates as suggested in format description */

		snprintf(buf, sizeof(buf), "%.6f\01%.6f\01%ld", lat, lon, (long)time);
		if (avltree_find(trkpts, buf, NULL)) continue;

		wpt = waypt_new();

		wpt->latitude = lat;
		wpt->longitude = lon;
		wpt->creation_time = time;
		wpt->microseconds = micros;
		if (speed >= 0) WAYPT_SET(wpt, speed, speed);
		if (mcourse >= 0) {
			course = mcourse;
			if (mvar >= 0) {
				if (mvardir == 'W') course += mvar;
				else if (mvardir == 'E') course -= mvar;
			}
			if (mdev >= 0) {
				if (mdevdir == 'W') course += mdev;
				else if (mdevdir == 'E') course -= mdev;
			}
		}
		if (course >= 0) WAYPT_SET(wpt, course, course);

		if (trk == NULL) {
			trk = route_head_alloc();
			track_add_head(trk);
		}

		avltree_insert(trkpts, buf, wpt);
		track_add_wpt(trk, wpt);
	}
}

static void
jtr_wr_init(const char *fname)
{
	fout = gbfopen(fname, "wb", MYNAME);
}

static void
jtr_wr_deinit(void)
{
	gbfclose(fout);
}

static void
jtr_trkpt_disp_cb(const waypoint *wpt)
{
	char *str, *tmp;
	char stime[10], sdate[7], scourse[6], sspeed[8];
	struct tm tm;

	if (wpt->creation_time > 0) {
		tm = *gmtime(&wpt->creation_time);
		tm.tm_year += 1900;
		tm.tm_mon += 1;
		snprintf(sdate, sizeof(sdate), "%02d%02d%02d", tm.tm_mday, tm.tm_mon, tm.tm_year % 100);
		snprintf(stime, sizeof(stime), "%02d%02d%02d.%02d", tm.tm_hour, tm.tm_min, tm.tm_sec, wpt->microseconds / 10000);
		if (wpt->microseconds / 10000 == 0) stime[6] = 0;
	}
	else {
		stime[0] = 0;
		sdate[0] = 0;
	}
	if (WAYPT_HAS(wpt, speed) && (wpt->speed >= 0))
		snprintf(sspeed, sizeof(sspeed), "%.1f", MPS_TO_KNOTS(wpt->speed));
	else sspeed[0] = 0;
	if (WAYPT_HAS(wpt, course) && (wpt->course >= 0))
		snprintf(scourse, sizeof(scourse), "%.1f", wpt->course);
	else scourse[0] = 0;

	xasprintf(&str, "GEOTAG2,%s,%c,%09.4f,%c,%010.4f,%c,%s,%s,%s,,E,A",
		stime,
		time > 0 ? 'A' : 'V',
		fabs(degrees2ddmm(wpt->latitude)),
		wpt->latitude < 0 ? 'S' : 'N',
		fabs(degrees2ddmm(wpt->longitude)),
		wpt->longitude < 0 ? 'W' : 'E',
		sspeed,
		scourse,
		sdate);

	xasprintf(&tmp, "%s*%02X", str, nmea_cksum(str));
	xfree(str);
	str = tmp;

	xasprintf(&tmp, "%s,,,E,,E*%02X\r", str, nmea_cksum(str));
	xfree(str);
	str = tmp;

	gbfputs(str, fout);
	xfree(str);
}

static void
jtr_write(void)
{
	track_disp_all(NULL, NULL, jtr_trkpt_disp_cb);
}

/**************************************************************************/

ff_vecs_t jtr_vecs = {
	ff_type_file,
	{
	  	ff_cap_none, 			/* waypoints */
		ff_cap_read | ff_cap_write, 	/* tracks */
	  	ff_cap_none 			/* routes */
	},
	jtr_rd_init,
	jtr_wr_init,
	jtr_rd_deinit,
	jtr_wr_deinit,
	jtr_read,
	jtr_write,
	NULL,
	jtr_args,
	CET_CHARSET_ASCII, 0			/* ascii is the expected character set */
						/* not fixed, can be changed through command line parameter */
};

/**************************************************************************/
